package com.wbs.domain.repository.specification;

import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import com.wbs.domain.util.CaseType;
import com.wbs.domain.util.DataPatternUtil;

/**
 * Abstract Class responsible for obtaining values from the ConditionFunction
 */
@SuppressWarnings("unchecked")
public abstract class AbstractSpecification {

    interface WhereConditionFunction {
       <T> Predicate getWhereCondition(CriteriaBuilder builder, Expression<T> field ,T value);
    }

    public static class Equal implements WhereConditionFunction{
        public <T> Predicate getWhereCondition(CriteriaBuilder builder, Expression<T> field, T value) {
             return builder.equal(field, value);
        }
    }

    public static class EqualId implements WhereConditionFunction{
        public <T> Predicate getWhereCondition(CriteriaBuilder builder, Expression<T> field, T value) {
             Path<T> path = (Path<T>) field;
             return builder.equal(path.get("id"), value);
        }
    }

    public static class Like implements WhereConditionFunction{
        
        public <T> Predicate getWhereCondition(CriteriaBuilder builder, Expression<T> field, T value) {
             String pattern = DataPatternUtil.buildJpaLikePattern((String) value, CaseType.UPPER);
             return builder.like(builder.upper((Expression<String>) field), pattern);
        }
    }

    public static class GreaterThanOrEqualTo implements WhereConditionFunction{
        public <T> Predicate getWhereCondition(CriteriaBuilder builder, Expression<T> field, T value) {
             Predicate resultPredicate = null;
             if (value instanceof java.util.Date){
                 resultPredicate = builder.greaterThanOrEqualTo((Expression<Date>)field, (Date)value);
             }
             if (value instanceof org.joda.time.DateTime){
                 resultPredicate = builder.greaterThanOrEqualTo((Expression<org.joda.time.DateTime>)field, (org.joda.time.DateTime)value);
             }
             return resultPredicate;
        }
    }

    public static class LessThanOrEqualTo implements WhereConditionFunction{
        public <T> Predicate getWhereCondition(CriteriaBuilder builder, Expression<T> field, T value) {
             Predicate resultPredicate = null;
             if (value instanceof java.util.Date){
                 resultPredicate = builder.lessThanOrEqualTo((Expression<Date>)field, (Date)value);
             }
             if (value instanceof org.joda.time.DateTime){
                 resultPredicate = builder.lessThanOrEqualTo((Expression<org.joda.time.DateTime>)field, (org.joda.time.DateTime)value);
             }
             return resultPredicate;
        }
    }

    @SuppressWarnings("rawtypes")
    public static <T> Predicate getPredicate(Map<String, Object> filters, Root<T> root,
                                      CriteriaBuilder builder,  Map<String, WhereConditionFunction> filterMap,
                                      Map<String, String> entityFieldsMap){
        Predicate resultPredicate = builder.equal(root.get("deleted"), false);
        Iterator iterator = filters.entrySet().iterator();

        while (iterator.hasNext()) {
            Map.Entry mapEntry = (Map.Entry) iterator.next();
            String key = (String) mapEntry.getKey();
            T value =  (T) mapEntry.getValue();
            if (value != null){
                String keyNew = entityFieldsMap!=null?(entityFieldsMap.get(key)!=null?entityFieldsMap.get(key):key):key;
                resultPredicate = builder.and(resultPredicate, filterMap.get(key).getWhereCondition(builder, root.get(keyNew), value));
            }
        }

        return resultPredicate;
    }
}